package com.lambkit.component.shiro.session;

import java.io.Serializable;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import com.lambkit.Lambkit;
import com.lambkit.common.util.StringUtils;
import com.lambkit.plugin.jwt.JwtConfig;

/**
 * 
 * @author yangyong
 * 
 *         目的: shiro 的 session 管理 自定义session规则，实现前后分离，在跨域等情况下使用token
 *         方式进行登录验证才需要，否则没必须使用本类。 shiro默认使用 ServletContainerSessionManager 来做
 *         session 管理，它是依赖于浏览器的 cookie 来维护 session 的,调用 storeSessionId
 *         方法保存sesionId 到 cookie中 为了支持无状态会话，我们就需要继承 DefaultWebSessionManager
 *         自定义生成sessionId 则要实现 SessionIdGenerator
 *
 */
public class ShiroSessionManager extends DefaultWebSessionManager {

	private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
	/**
     * 返回客户端的，
     */
    public static final String TOKEN_NAME = "Lambkit-Token";
	
	private static final Log log = Log.getLog(ShiroSessionManager.class);

	public ShiroSessionManager() {
		super();
	}

	/**
	 * 获取sessionId，原本是根据sessionKey来获取一个sessionId<br/>
	 * 重写的部分多了一个把获取到的token设置到request的部分。<br/>
	 * 这是因为app调用登陆接口的时候，是没有token的，登陆成功后，产生了token,<br/>
	 * 我们把它放到request中，返回结果给客户端的时候，把它从request中取出来，并且传递给客户端，<br/>
	 * 客户端每次带着这个token过来，就相当于是浏览器的cookie的作用，也就能维护会话了<br/>
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	@Override
	protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
		// TODO Auto-generated method stub
		String sessionId = request.getParameter("tk");
		sessionId = StrKit.isBlank(sessionId) ? request.getParameter("token") : sessionId;
		sessionId = StrKit.isBlank(sessionId) ? request.getParameter("accessToken") : sessionId;
		sessionId = StrKit.isBlank(sessionId) ? request.getParameter("sessionId") : sessionId;
		sessionId = StrKit.isBlank(sessionId) ? request.getParameter("sessionid") : sessionId;
		if (StrKit.isBlank(sessionId)) {
			JwtConfig config = Lambkit.config(JwtConfig.class);
			String header = config.getHeader();
			sessionId = WebUtils.toHttp(request).getHeader(header);
			// System.out.println("shiro session manager [header: " + header + ", sessionId:
			// " + sessionId + ", time: " + DateTimeUtils.getCurrentTimeLong());
		}
		if (StrKit.isBlank(sessionId)) {
			sessionId = WebUtils.toHttp(request).getHeader(TOKEN_NAME);
		}
		/*
		 * JwtConfig config = Lambkit.config(JwtConfig.class); String header =
		 * config.getHeader(); //获取请求头中的 AUTH_TOKEN 的值，如果请求头中有 AUTH_TOKEN
		 * 则其值为sessionId。shiro就是通过sessionId 来控制的 String sessionId =
		 * WebUtils.toHttp(request).getHeader(header); sessionId =
		 * StringUtils.isBlank(sessionId) ? request.getParameter("sessionid") :
		 * sessionId;
		 */
		String id = this.getSessionIdCookieValue(request, response);
		System.out.println("ShiroSessionManager reset sessionId: " + sessionId + ", cookie: " + id);
		if (StringUtils.isBlank(sessionId)) {
			// 如果没有携带id参数则按照父类的方式在cookie进行获取sessionId
			return super.getSessionId(request, response);
		} else {
			// 请求头中如果有 authToken, 则其值为sessionId
			request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
			// sessionId
			request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);
			request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
			return sessionId;
		}
	}

	@Override
	protected void onStart(Session session, SessionContext context) {
		// TODO Auto-generated method stub
		// super.onStart(session, context);
		System.out.println("执行onStart");
		if (!WebUtils.isHttp(context)) {
			log.debug(
					"SessionContext argument is not HTTP compatible or does not have an HTTP request/response pair. No session ID cookie will be set.");
		} else {
			HttpServletRequest request = WebUtils.getHttpRequest(context);
			HttpServletResponse response = WebUtils.getHttpResponse(context);
			Serializable sessionId = session.getId();
			this.storeSessionId(sessionId, request, response);
			request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
			request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
		}
	}
	
	/**
     * 把sessionId 放入 response header 中
     * onStart时调用
     * 没有sessionid时 会产生sessionid 并放入 response header中
     */
    private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
        if (currentId == null) {
            String msg = "sessionId cannot be null when persisting for subsequent requests.";
            throw new IllegalArgumentException(msg);
        } else {
            String idString = currentId.toString();
            response.setHeader(TOKEN_NAME, idString);
            
            if (isSessionIdCookieEnabled()) {
            	Cookie template = getSessionIdCookie();
                Cookie cookie = new SimpleCookie(template);
                cookie.setValue(idString);
                cookie.saveTo(request, response);
	        } else {
	            log.debug("Session ID cookie is disabled.  No cookie has been set for new session with id " + idString);
	        }
            
            log.info("Set session ID header for session with id " + idString);
            System.out.println("Set session ID header for session with id " + idString);
        }
    }

	private String getPara(String string) {
		// TODO Auto-generated method stub
		return null;
	}

	// copy super
	private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
		if (!this.isSessionIdCookieEnabled()) {
			return null;
		} else if (!(request instanceof HttpServletRequest)) {
			return null;
		} else {
			HttpServletRequest httpRequest = (HttpServletRequest) request;
			return this.getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
		}
	}
}
